﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;

namespace NTrace.VSPackage
{
    public class ObservableDictionary<T, V> : IDictionary<T, V>, INotifyCollectionChanged
    {
        private Dictionary<T, V> innerDictionary;

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public ObservableDictionary()
        {
            this.innerDictionary = new Dictionary<T, V>();
        }

        public void Add(T key, V value)
        {
            this.innerDictionary.Add(key, value);
            this.OnCollectionChanged(NotifyCollectionChangedAction.Add, value);
        }

        public bool ContainsKey(T key)
        {
            return this.innerDictionary.ContainsKey(key);
        }

        public ICollection<T> Keys
        {
            get
            {
                return this.innerDictionary.Keys;
            }
        }

        public bool Remove(T key)
        {
            V value;
            bool output = this.innerDictionary.TryGetValue(key, out value);
            if (output)
            {
                output &= this.innerDictionary.Remove(key);
                this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, value);
            }
            return output;
        }

        public bool TryGetValue(T key, out V value)
        {
            return this.innerDictionary.TryGetValue(key, out value);
        }

        public ICollection<V> Values
        {
            get
            {
                return this.innerDictionary.Values;
            }
        }

        public V this[T key]
        {
            get
            {
                return this.innerDictionary[key];
            }
            set
            {
                V currentValue;
                bool keyExisted = this.TryGetValue(key, out currentValue);
                if (keyExisted && this.CompareValues(value, currentValue))
                {
                    return;
                }
                this.innerDictionary[key] = value;
                this.OnCollectionChanged(keyExisted ? NotifyCollectionChangedAction.Replace : NotifyCollectionChangedAction.Add, value);
            }
        }

        public void Add(KeyValuePair<T, V> item)
        {
            ((IDictionary<T, V>)this.innerDictionary).Add(item);
            this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item.Value);
        }

        public void Clear()
        {
            this.innerDictionary.Clear();
            this.OnCollectionChanged(NotifyCollectionChangedAction.Reset);
        }

        public bool Contains(KeyValuePair<T, V> item)
        {
            return ((IDictionary<T, V>)this.innerDictionary).Contains(item);
        }

        public void CopyTo(KeyValuePair<T, V>[] array, int arrayIndex)
        {
            ((IDictionary<T, V>)this.innerDictionary).CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get
            {
                return this.innerDictionary.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return ((IDictionary<T, V>)this.innerDictionary).IsReadOnly;
            }
        }

        public bool Remove(KeyValuePair<T, V> item)
        {
            bool output = ((IDictionary<T, V>)this.innerDictionary).Remove(item);
            if (output) this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);
            return output;
        }

        public IEnumerator<KeyValuePair<T, V>> GetEnumerator()
        {
            return ((IDictionary<T, V>)this.innerDictionary).GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        private void OnCollectionChanged(NotifyCollectionChangedAction action)
        {
            System.Diagnostics.Debug.Assert(action == NotifyCollectionChangedAction.Reset);
            NotifyCollectionChangedEventHandler temp = this.CollectionChanged;
            if (temp != null)
            {
                temp(this, new NotifyCollectionChangedEventArgs(action));
            }
        }

        private void OnCollectionChanged(NotifyCollectionChangedAction action, Object item) 
        {
            NotifyCollectionChangedEventHandler temp = this.CollectionChanged;
            if (temp != null)
            {
                temp(this, new NotifyCollectionChangedEventArgs(action, item));
            }
        }

        private bool CompareValues(V value, V currentValue)
        {
            return (currentValue != null && currentValue.Equals(value)) ||
                   (value != null && value.Equals(currentValue) ||
                   value == null && currentValue == null);
        }
    }
}
